// 封装 Socket -- 基于模版类设计模式

#pragma once

#include <iostream>
#include "Log.hpp"
#include "Common.hpp"
#include "InteInfo.hpp"

namespace SocketModel
{
    using namespace LogModel;

    const int default_backlog = 8;
    const int default_sockfd = -1;

    // 这是一个基类 , 通过多态外部可以通过其类创建 TCP , UDP
    // 实现多态的前提是继承 , 并且要重写基类的虚函数
    // 这里定义纯虚函数 , 让基类中不用具体实现 , 继承多态时具体实现
    class Socket
    {
    public:
        Socket() {}
        virtual ~Socket() {}

        // 1 . UDP和TCP 都要创建套接字吧
        virtual void SocketOrDie() = 0;
        // 2 . UDP和TCP 都要绑定吧 , 只有服务器会显示Bind , 所以传端口 , IP初始化为 0
        virtual void BindOrDie(uint16_t port) = 0;
        // 3 . TCP需要监听
        virtual void ListenOrDie(int backlog) = 0;
        // 4.  TCP需要获取连接 - accept
        // 返回一个Socket的指针,方便使用子类 , 传参指针将信息带出去
        // 返回值用 shared_ptr 因为其支持拷贝
        virtual std::shared_ptr<Socket> Accept(InteInfo *client) = 0;

        // 5. 关闭文件描述符
        virtual void Close() = 0;

        // 套接字不仅仅服务端用 , 客户端也要用

        // 6. 客户端向服务端建立连接请求 , 客户端有服务端的ip和端口
        virtual int Connect(const std::string &server_ip, uint16_t port) = 0;

        // 7. 客户端也要接受消息
        virtual int Recv(std::string *out) = 0;
        // 8. 客户端也要发消息
        virtual int Send(const std::string &message) = 0;

    public:
        // 这里提供创建TCP和创建UDP的接口

        // 1. 创建TCP服务端 , 从右到左依次缺省可以 , 但是从左到右不行 !!!!!!!!!
        void BuildTcpSocketMethod(uint16_t port, int backlog = default_backlog)
        {
            SocketOrDie();
            BindOrDie(port);
            ListenOrDie(backlog);
        }

        // 2. 创建UDP服务端
        void BuildUdpSocketMethod(uint16_t port)
        {
            SocketOrDie();
            BindOrDie(port);
        }

        // 3. 创建TCP客户端
        void BuildTcpClientSocketMethod()
        {
            SocketOrDie();
        }
    };

    // UDP 继承基类 - 子类就要重写虚函数
    #if 0 
    class UDP : public Socket
    {
    public:
        UDP() {}
        // 无论子类析构有无 virtual 都与基类的虚析构构成重写 !
        ~UDP() {}

        // 1. 重写 SocketOrDie()
        void SocketOrDie() override
        {
        }

        // 2. 重写 BindOrDie()
        void BindOrDie(uint16_t port) override
        {
        }

        // 3. 重写 ListenOrDie()
        void ListenOrDie(int backlog) override
        {
            // 对 UDP来说 ,不用做这个 , 所以什么也不干
        }

        //..... 其它方法看情况是否实现 , 不实现但是也要有 , 不实现的为空即可
    };
    #endif 

    // 主要实现 TCP
    //  TCP 继承基类 - 子类就要重写虚函数
    class TCP : public Socket
    {
    public:
        TCP() {}
        // 重载构造函数
        TCP(int sockfd) // 普通文件描述符
            : _sockfd(sockfd)
        {
        }

        TCP()
            : _sockfd(default_sockfd)
        {
        }
        //  无论子类析构有无 virtual 都与基类的虚析构构成重写 !
        virtual ~TCP() {}

        // 1. 重写 SocketOrDie()
        void SocketOrDie() override
        {
            // 创建套接字 -- TCP面向字节流
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(LogLevel::FATAL) << "socket error";
                exit(SOCKET_ERROR);
            }
            LOG(LogLevel::INFO) << "socket success";
        }

        // 2. 重写 BindOrDie() -- bind 只有服务器才会
        // 所以 , 服务器绑定 IP:0 , 和端口号
        void BindOrDie(uint16_t port) override
        {
            // 构建网络信息
            InteInfo local(port);
            int n = ::bind(_sockfd, local.InteAddrToSockAddr(), local.SockAddrLen());
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "bind error";
                exit(BIND_ERROR);
            }
            LOG(LogLevel::INFO) << "bind success";
        }

        // 3. 重写 ListenOrDie()
        void ListenOrDie(int backlog) override
        {
            int n = ::listen(_sockfd, backlog);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "listen error";
                exit(LISTEN_ERROR);
            }
            LOG(LogLevel::INFO) << "listen success";
        }

        // 4. 重写 Accept() -- 返回值用 shared_ptr 因为其支持拷贝
        std::shared_ptr<Socket> Accept(InteInfo *client) override
        {
            // accept 会将对端的网络信息带回来
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 获取连接 - //此时 peer 中存的就是对端的网络信息
            int fd = ::accept(_sockfd, CONV(peer), &len);
            if (fd < 0)
            {
                LOG(LogLevel::WARNING) << "accept warning ...";
                return nullptr;
            }
            // 将信息带出去
            client->SetAddr(peer); // 将对端信息带出去 , 方便外面使用
            // 这里返回 TCP对象 , 这样基类指向派生类, 可以访问派生类
            return std::make_shared<TCP>(fd); // 这个fd就是完成任务的fd
        }

        // 5.
        void Close() override
        {
            if (_sockfd >= 0)
                ::close(_sockfd);
        }

        // 6. 给客户端使用的接口
        int Connect(const std::string &server_ip, uint16_t port) override
        {
            // 构建网络信息
            InteInfo peer(server_ip, port);
            return ::connect(_sockfd, peer.InteAddrToSockAddr(), peer.SockAddrLen());
        }

        // 7.
        int Recv(std::string *out) override
        {
            char buffer[1024];
            ssize_t n = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                *out += buffer; // 故意 , 一直让缓冲区增加内容 , 因为TCP协议可能读不完整
            }
            return n;
        }

        // 8.
        int Send(const std::string &message) override
        {
            return send(_sockfd, message.c_str(), message.size(), 0);
        }

    private:
        int _sockfd; // 文件描述符 , 可能是普通的 , 可能是listenfd
    };
};